#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "2d5p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"

__thread_local volatile unsigned long get_reply,put_reply;


__thread_local_fix double A_lhs[BLKX+2][BLKY+2];
__thread_local_fix double A_rhs[BLKX+2][BLKY+2];
//Record the logical information of this time executed task
__thread_local_fix int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj;
//Record the logical information of the last time executed task.
__thread_local_fix int last_iter,last_starti,last_startj;

extern double A[2][SIZEX][SIZEY];

#define alpha_d 0.0876
#define beta_d  0.0765

#ifdef _PROFILING
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start1,dma_end1,dma_start2,dma_end2,dma_start3,dma_end3,dma_start4,dma_end4;
__thread_local volatile unsigned long comp_start,comp_end,comp_start1,comp_end1;
__thread_local int core_id=0;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
extern unsigned long slave_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

void stencil_core(targs* args)
{
   //long  array_size;
   int tsize, bsize, stride;
   s_iter = args->iter;
   dest = (s_iter) % 2 ; 
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj)
   {
#ifdef _PROFILING
   dma_start1=rtc_();
#endif
      //int len, bsize, stride;
      get_reply = 0;
      //1.1 update up and down shade data
      athread_get(PE_MODE,&A[src][s_starti-1][s_startj],&A_lhs[0][1],(BLKY)*8,(void*)&get_reply,0,0,0); 
      athread_get(PE_MODE,&A[src][s_endi][s_startj], &A_lhs[BLKX+1][1],(BLKY)*8,(void*)&get_reply,0,0,0);
      while(get_reply!=2);
      //left shade (mmem->rhs)
      get_reply=0;
      stride = (SIZEY-1)*8;
      bsize = 8;
      tsize = BLKX*8;
      athread_get(PE_MODE,&A[src][s_starti][s_startj-1]
                     , &A_rhs[0][0]
                     ,tsize,(void*)&get_reply,0,stride,bsize);
      while(get_reply!=1);
      //left shade (rhs->lhs)
      for(i=1;i<BLKX+2;++i)
        A_lhs[i][0]=A_rhs[0][i-1];
      //right shade (mmem->rhs)
      get_reply=0;
      athread_get(PE_MODE,&A[src][s_starti][s_endj]
                     , &A_rhs[0][0]
                     ,tsize,(void*)&get_reply,0,stride,bsize); 
      while(get_reply != 1);
      //right shade (rhs->lhs)
      for(i=1;i<BLKX+2;++i)
        A_lhs[i][BLKY+1]=A_rhs[0][i-1];
#ifdef _PROFILING
   dma_end1=rtc_();
   slave_dma_count[core_id]+=(dma_end1-dma_start1);
   dma_end1=dma_start1=0;
   comp_start=rtc_();   
#endif
      //comp
     /* for(i = 1; i < BLKX+1; i++)
        for(j = 1; j < BLKY+1; j++)
         A_rhs[i][j] = alpha_d * (A_lhs[i][j]) + \
                     beta_d * (A_lhs[i-1][j] + A_lhs[i+1][j] +\
                     A_lhs[i][j-1] + A_lhs[i][j+1]);*/
       for(i = 1; i < BLKX+1; i++)
        for(j = 1; j < BLKY-BLKY%4; j+=4)
        {
           A_rhs[i][j] = alpha_d * (A_lhs[i][j]) + \
                     beta_d * (A_lhs[i-1][j] + A_lhs[i+1][j] +\
                     A_lhs[i][j-1] + A_lhs[i][j+1]);
           A_rhs[i][j+1] = alpha_d * (A_lhs[i][j+1]) + \
                     beta_d * (A_lhs[i-1][j+1] + A_lhs[i+1][j+1] +\
                     A_lhs[i][j] + A_lhs[i][j+2]);
           A_rhs[i][j+2] = alpha_d * (A_lhs[i][j+2]) + \
                     beta_d * (A_lhs[i-1][j+2] + A_lhs[i+1][j+2] +\
                     A_lhs[i][j+1] + A_lhs[i][j+3]);
           A_rhs[i][j+3] = alpha_d * (A_lhs[i][j+3]) + \
                     beta_d * (A_lhs[i-1][j+3] + A_lhs[i+1][j+3] +\
                     A_lhs[i][j+2] + A_lhs[i][j+4]);
        }
       for(i = 1; i < BLKX+1; i++)
        for(j = BLKY-BLKY%4; j<BLKY+1; j++)
          A_rhs[i][j] = alpha_d * (A_lhs[i][j]) + \
                     beta_d * (A_lhs[i-1][j] + A_lhs[i+1][j] +\
                     A_lhs[i][j-1] + A_lhs[i][j+1]);
#ifdef _PROFILING
   comp_end=rtc_();
   slave_comp_count[core_id]+=(comp_end-comp_start);
   comp_end=comp_start=0;
   dma_start2=rtc_();
#endif
      //write back
      put_reply=0;
      for(i =  s_starti; i < s_endi; ++i)
      athread_put(PE_MODE,&A_rhs[i-s_starti+1][1]
                     , &A[dest][i][s_startj]
                     ,(BLKY)*8,(void*)&put_reply,0,0);
      while(put_reply!=(BLKX));
#ifdef _PROFILING
   dma_end2=rtc_();
   slave_dma_count[core_id]+=((dma_end2-dma_start2));
   dma_end2=dma_start2=0;
#endif
   }else{
   // 1.fetch data
#ifdef _PROFILING
   dma_start3=rtc_();
#endif
   get_reply=0;  
   bsize=(BLKY+2)*8;
   stride=(SIZEY-(BLKY+2))*8;  //stride: from blocktail to blockhead 
   tsize= bsize*(BLKX+2);
   athread_get(PE_MODE,&A[src][s_starti-1][s_startj-1]
                 , &A_rhs[0][0]
                 , tsize,(void*)&get_reply
                 , 0, stride, bsize);
   
   //2.synchronize, wait for the transfer finish
   while(get_reply!=1);
#ifdef _PROFILING
   dma_end3=rtc_();
   slave_dma_count[core_id]+=(dma_end3-dma_start3);
   dma_end3=dma_start3=0;
   comp_start1=rtc_();
#endif
   //comp
   /*for(i = 1; i < BLKX+1; i++)
     for(j = 1; j < BLKY+1; j++)
        A_lhs[i][j] = alpha_d * A_rhs[i][j] + \
                     beta_d * (A_rhs[i-1][j] + A_rhs[i+1][j] +\
                     A_rhs[i][j-1] + A_rhs[i][j+1]);*/
    for(i = 1; i < BLKX+1; i++)
        for(j = 1; j < BLKY-BLKY%4; j+=4)
        {
           A_lhs[i][j] = alpha_d * (A_rhs[i][j]) + \
                     beta_d * (A_rhs[i-1][j] + A_rhs[i+1][j] +\
                     A_rhs[i][j-1] + A_rhs[i][j+1]);
           A_lhs[i][j+1] = alpha_d * (A_rhs[i][j+1]) + \
                     beta_d * (A_rhs[i-1][j+1] + A_rhs[i+1][j+1] +\
                     A_rhs[i][j] + A_rhs[i][j+2]);
           A_lhs[i][j+2] = alpha_d * (A_rhs[i][j+2]) + \
                     beta_d * (A_rhs[i-1][j+2] + A_rhs[i+1][j+2] +\
                     A_rhs[i][j+1] + A_rhs[i][j+3]);
           A_lhs[i][j+3] = alpha_d * (A_rhs[i][j+3]) + \
                     beta_d * (A_rhs[i-1][j+3] + A_rhs[i+1][j+3] +\
                     A_rhs[i][j+2] + A_rhs[i][j+4]);
        }
       for(i = 1; i < BLKX+1; i++)
        for(j = BLKY-BLKY%4; j<BLKY+1; j++)
          A_lhs[i][j] = alpha_d * (A_rhs[i][j]) + \
                     beta_d * (A_rhs[i-1][j] + A_rhs[i+1][j] +\
                     A_rhs[i][j-1] + A_rhs[i][j+1]);
#ifdef _PROFILING
   comp_end1=rtc_();
   slave_comp_count[core_id]+=(comp_end1-comp_start1);
   comp_end1=comp_start1=0;
   dma_start4=rtc_();
#endif
   //write back
    put_reply=0;
    for(i =  s_starti; i < s_endi; ++i)
       athread_put(PE_MODE,&A_lhs[i-s_starti+1][1]
                     , &A[dest][i][s_startj]
                     ,(BLKY)*8,(void*)&put_reply,0,0);
   while(put_reply!=(BLKX));
#ifdef _PROFILING
   dma_end4=rtc_();
   slave_dma_count[core_id]+=((dma_end4-dma_start4));
   dma_end4=dma_start4=0;
#endif
   }
   last_iter = s_iter;
   last_starti = s_starti;
   last_startj = s_startj;
}
